package poolserver

import (
	"time"

	"go.uber.org/zap"
)

/*把所有池中的连接超过一定时间后，重建连接，以保证这些连接是新鲜的*/
func freshPools() {
	var pool *Pool
	var backConn *PgBackendConn
	var cnt int
	var i int

	for {
		time.Sleep(10 * time.Second)
		PoolsLock.RLock()
		for _, pool = range g_allpools {
			pool.Lock.RLock()
			for portal, bePool := range pool.BePools {
				bePool.Lock.Lock()
				cnt = len(bePool.BeConns)
				beState := bePool.State
				bePool.Lock.Unlock()
				if beState != BE_ONLINE { /*跳过上线状态的bePool*/
					continue
				}
				i = 0
			FOR:
				for {
					/*从srcChan中获得一个空闲连接，处理完后放回dstChan中*/
					select {
					case backConn = <-bePool.FreeChan:
						/*清除已经记录为需要关闭的prepare*/
						backConn.CleanupNeedClosePrepare()
						/*把连接的时间超过了lifetime时，重新建立*/
						if time.Now().Unix()-backConn.ConnUnixTime > int64(pool.BeConnLifeTime) {
							zap.S().Infof("backConn(%d) life cycle has ended, refresh connection %s", backConn.Id, portal)
							backConn.FreshConn(pool)
							backConn.LifeUsedCount = 0
						}
						bePool.FreeChan <- backConn
						i++
						if i > cnt {
							break FOR
						}
					default:
						break FOR
					}
				}
			}
			pool.Lock.RUnlock()
		}
		PoolsLock.RUnlock()
	}
}

func GetRealPrimary(pool *Pool, PriPools map[string]*BePool) string {
	var priPortal string = ""
	var max_tl int32 = 0
	var max_lsn int64 = 0

	for portal := range PriPools {
		conn, err, _, _ := connectBackend(portal, pool.BeUser, pool.BePasswd, pool.BeDbName)
		if err != nil {
			zap.S().Errorf("Could not connect %s: %s", portal, err.Error())
			continue
		}
		err, tl, lsn := getTimelineAndLsn(conn)
		conn.Close()
		if err != nil {
			zap.S().Errorf("Could not get timeline and lsn from %s: %s", portal, err.Error())
			continue
		}
		if tl >= max_tl {
			if tl > max_tl {
				max_tl = tl
				max_lsn = lsn
				priPortal = portal
			} else if tl == max_tl {
				if lsn > max_lsn {
					max_lsn = lsn
					priPortal = portal
				}
			}
		}
	}
	return priPortal
}

// BePoolRecoveryPending 修复故障
func BePoolRecoveryPending() {
	var pool *Pool
	//var bePool * bePool
	var beState int8
	var needRemovedbePools map[string]*BePool
	var badBePools map[string]*BePool
	var multiPrimary map[string]*BePool
	var realPortal string

	for {
		time.Sleep(2 * time.Second)
		PoolsLock.RLock()
		for _, pool = range g_allpools {
			needRemovedbePools = make(map[string]*BePool)
			badBePools = make(map[string]*BePool)
			pool.Lock.RLock()
			for portal, bePool := range pool.BePools {
				beState = bePool.GetState()
				if beState == BE_REMOVING { /*移除中*/
					needRemovedbePools[portal] = bePool
				} else if beState == BE_BAD_NO_FREE || beState == BE_BAD_FREEED { /*坏的Pool*/
					badBePools[portal] = bePool
				}
			}
			pool.Lock.RUnlock()
			/*清理需要移除的bePool*/
			for portal, bePool := range needRemovedbePools {
				bePool.Lock.Lock()
				if len(bePool.FreeChan) == len(bePool.BeConns) { /*如果所有的连接都是空闲时，可以清理*/
					for _, backConn := range bePool.BeConns {
						backConn.Conn.Close()
					}
					close(bePool.FreeChan)
					bePool.Lock.Unlock()
					pool.Lock.Lock()
					delete(pool.BePools, portal)
					pool.Lock.Unlock()
				} else {
					bePool.Lock.Unlock()
				}
			}

			/*处理状态为坏的bePool*/
			multiPrimary = make(map[string]*BePool)
			for portal, bePool := range badBePools {
				bePool.Lock.Lock()
				if bePool.State == BE_BAD_NO_FREE {
					if len(bePool.FreeChan) == len(bePool.BeConns) { /*如果所有的连接都是空闲时，可以清理*/
						for _, backConn := range bePool.BeConns {
							backConn.Conn.Close()
						}
						close(bePool.FreeChan)
						bePool.State = BE_BAD_FREEED
					}
				}
				bePool.Init()
				err := BuildBePoolConn(pool, portal, bePool)
				if err != nil {
					bePool.Lock.Unlock()
					continue
				}
				//执行BuildBePoolConn()成功之后，如果此节点仍然是坏的节点，这时需要把状态改成BE_BAD_NO_FREE，如果不把状态设置为BE_BAD_NO_FREE，
				//  则之前的状态可能是BE_BAD_FREEED，导致下一次循环时不会关闭连接，从而导致连接泄漏*/
				bePool.State = BE_BAD_NO_FREE

				if bePool.RwState == DB_READWRITE {
					/*如果是主库，则记录到multiPrimary中，后续需要检查是否有两个主库的情况*/
					multiPrimary[portal] = bePool
				} else {
					bePool.State = BE_ONLINE
				}
				bePool.Lock.Unlock()
			}

			/*这里开始判断是主库的情况*/
			if len(multiPrimary) > 0 {
				/*检查是否存在多个主库，则需要判断哪个主库才是真正的主库*/
				for tmp_portal, tmp_bePool := range pool.BePools {
					_, ok := multiPrimary[tmp_portal]
					if ok {
						continue
					}
					tmp_bePool.Lock.Lock()
					if tmp_bePool.State == BE_REMOVING || tmp_bePool.State == BE_OFFLINE {
						tmp_bePool.Lock.Unlock()
						continue
					}
					if tmp_bePool.RwState == DB_READWRITE {
						multiPrimary[tmp_portal] = tmp_bePool
					}
					tmp_bePool.Lock.Unlock()
				}

				if len(multiPrimary) > 1 { /*存在多个主库，则需要判断哪个主库才是真正的主库*/
					realPortal = GetRealPrimary(pool, multiPrimary)
					zap.S().Warnf("some portal (%v) is a primary database, but real database primary is %s", multiPrimary, realPortal)
					for portal, bePool := range multiPrimary {
						if portal == realPortal { // 这时真的主库
							bePool.Lock.Lock()
							bePool.State = BE_ONLINE
							bePool.Lock.Unlock()
						} else { //不是真主库的，把状态设置为坏
							bePool.Lock.Lock()
							if bePool.State == BE_ONLINE {
								bePool.State = BE_BAD_NO_FREE
							}
							bePool.Lock.Unlock()
						}
					}
				} else { /*如果只有一个主库，则认为这个主库是正常的主库*/
					for _, bePool := range multiPrimary {
						bePool.Lock.Lock()
						bePool.State = BE_ONLINE
						bePool.Lock.Unlock()
					}
				}
			}
		}
		PoolsLock.RUnlock()
	}
}

/*检查状态是OK的池的健康情况，此函数只会检查状态为好的portal，如果发现其为坏，把状态改成坏，不会把坏的portal改成好*/
func checkPoolsHealth() {
	var pool *Pool
	var beState int8
	var retryCnt int32 = 0
	var rwState int8
	var multiPrimary map[string]*BePool
	var realPortal string

	for {
		time.Sleep(10 * time.Second)
		PoolsLock.RLock()
		for _, pool = range g_allpools {
			//multiPrimary中放好的主库
			multiPrimary = make(map[string]*BePool)
			pool.Lock.RLock()
			for portal, bePool := range pool.BePools {
				bePool.Lock.Lock()
				beState = bePool.State
				rwState = bePool.RwState
				bePool.Lock.Unlock()
				if beState != BE_ONLINE { /*跳过非上线状态的bePool*/
					continue
				}

				retryCnt = 0
				reconn_is_ok := true
				var tmpRwState int8 = 0
				for {
					conn, err, _, _ := connectBackend(portal, pool.BeUser, pool.BePasswd, pool.BeDbName)
					if err == nil {
						err, tmpRwState = getDbReadWriteState(conn)
						conn.Close()
					}
					if err == nil {
						break
					}
					time.Sleep(time.Duration(pool.BeRetryInterval) * time.Second)
					retryCnt++
					if retryCnt > pool.BeRetryCnt { //多次重试后，仍然不能连接，则认为此portal坏掉了
						reconn_is_ok = false
						break
					}
				}

				if reconn_is_ok {
					if rwState == DB_READWRITE && tmpRwState == DB_READONLY {
						//原先时主，现在变成了备，说明此portal的坏了
						bePool.Lock.Lock()
						bePool.State = BE_BAD_NO_FREE
						bePool.Lock.Unlock()
					} else if rwState == DB_READONLY && tmpRwState == DB_READWRITE {
						multiPrimary[portal] = bePool
					} else if rwState == DB_READWRITE && tmpRwState == DB_READWRITE {
						multiPrimary[portal] = bePool
					}
				} else {
					bePool.Lock.Lock()
					bePool.State = BE_BAD_NO_FREE
					bePool.Lock.Unlock()
				}
			}

			if len(multiPrimary) > 1 { /*存在多个主库，则需要判断哪个主库才是真正的主库*/
				realPortal = GetRealPrimary(pool, multiPrimary)
				zap.S().Warnf("some portal (%v) is a primary database, but real database primary is %s", multiPrimary, realPortal)
				for portal, bePool := range multiPrimary {
					if portal == realPortal {
						bePool.Lock.Lock()
						bePool.RwState = DB_READWRITE
						bePool.Lock.Unlock()
					} else { /*把状态是主库，但不是真的主库的节点设置为坏*/
						bePool.Lock.Lock()
						bePool.State = BE_BAD_NO_FREE
						bePool.Lock.Unlock()
					}
				}
			} else if len(multiPrimary) == 1 { /*只有一个主库*/
				for _, bePool := range multiPrimary {
					bePool.Lock.Lock()
					bePool.RwState = DB_READWRITE
					bePool.Lock.Unlock()
				}
			}

			pool.Lock.RUnlock()
		}
		PoolsLock.RUnlock()
	}
}
